﻿#pragma once
#include "AsMathBasic.h"
#include <map>

namespace AsMath {

	template <typename _Type>
	bool AscendingOrder(_Type t1, _Type t2) {
		return t1 < t2;
	}

	template <typename _Type>
	bool DescendingOrder(_Type t1, _Type t2) {
		return t1 > t2;
	}

	template <typename _Type, typename _Rule> 
	void sort(std::vector<_Type>& inputArray, _Rule rule) {
		std::sort(inputArray.begin(), inputArray.end(), rule);
	}

	template <typename _Type, typename _XFunctor, typename _YFunctor>
	void sort2dDist(std::vector<_Type>& inputArray, const cv::Point2d& toPosition, _XFunctor xfunctor, _YFunctor yfunctor) {
		std::map<double, _Type> distMap;
		for (const auto& elem : inputArray) {
			double x = xfunctor(elem);
			double y = yfunctor(elem);
			distMap.insert(std::make_pair(AsMath::calcDistance(cv::Point2d(x, y), toPosition), elem));
		}
		inputArray.clear();
		for (const auto& elem : distMap) {
			inputArray.emplace_back(elem.second);
		}
	}

	template <typename _Type, typename _XFunctor, typename _YFunctor>
	void sort2dYChunk(std::vector<_Type>& inputArray, int chunkHeight, _XFunctor xfunctor, _YFunctor yfunctor) {

		std::vector<std::vector<_Type>> subsArrays;

		for (const auto& elem : inputArray) {
			int index = floor(yfunctor(elem) / chunkHeight);
			if (index >= subsArrays.size()) subsArrays.resize(index + 1);
			subsArrays[index].push_back(elem);
		}

		inputArray.clear();
		for (auto& subarrs : subsArrays) {
			std::sort(subarrs.begin(), subarrs.end(), [](const _Type& a1, const _Type& a2) {
				return xfunctor(a1) < xfunctor(a2);
			});
			AsMath::append(inputArray, subarrs);
		}
	}

	template <typename _Type, typename _XFunctor, typename _YFunctor>
	void sort2dYClassify(std::vector<_Type>& inputArray, double classify_thresh, _XFunctor xfunctor, _YFunctor yfunctor) {
		if (inputArray.empty()) return;

		std::sort(inputArray.begin(), inputArray.end(), [yfunctor](const _Type& a1, const _Type& a2) {
			return yfunctor(a1) < yfunctor(a2);
		});

		std::vector<std::vector<_Type>> classifys(1);

		double temp = yfunctor(inputArray.front());
		for (const auto& elem : inputArray) {
			double y = yfunctor(elem);
			if (y - temp > classify_thresh) {
				temp = y;
				classifys.push_back(std::vector<_Type>());
			}
			classifys.back().emplace_back(elem);
		}

		inputArray.clear();
		for (auto& classify : classifys) {
			std::sort(classify.begin(), classify.end(), [xfunctor](const _Type& a1, const _Type& a2) {
				return xfunctor(a1) < xfunctor(a2);
			});
			AsMath::append(inputArray, classify);
		}

	}

};